package org.freeocs.dispatcher;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.mail.Authenticator;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.Message.RecipientType;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.freeocs.Globals;
import org.freeocs.commons.mail.MailTransportQueue;
import org.freeocs.commons.util.StringUtils;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.Record;
import org.xbill.DNS.Type;

/**
 * 邮件传递服务程序
 * 
 * @author chirs
 */
public class FOCSMailSenderServlet extends GenericServlet implements Runnable {

	private static final long serialVersionUID = 4765471941565972505L;
	protected String smtp_host;
	protected int smtp_port = 25;
	protected String smtp_user;
	protected String smtp_pass;
	protected String mail_queue_path = "/WEB-INF/spool";

	private boolean userDNSQuery = true;
	private boolean stop = false;
	private Thread tMailSender;
	private MailTransportQueue queue = null;

	private Session mailSession;
	private final static Properties ocsProperties = new Properties();

	/**
	 * 初始化配置信息
	 */
	public void init() throws ServletException {
		super.init();

		initParams();

		if (StringUtils.isNotEmpty(smtp_host)) {
			userDNSQuery = false;
			Properties mailProperties = System.getProperties();
			mailProperties.put("mail.smtp.host", smtp_host);
			if (smtp_port > 0 && smtp_port != 25)
				mailProperties.put("mail.smtp.port", String.valueOf(smtp_port));
			mailProperties.put("mail.smtp.auth", "true"); // 设置smtp认证，很关键的一句
			mailSession = Session.getDefaultInstance(mailProperties, new Authenticator() {
				protected PasswordAuthentication getPasswordAuthentication() {
					return new PasswordAuthentication(smtp_user, smtp_pass);
				}
			});
		} else {
			log("特快专递");
			Properties props = new Properties();
			props.put("mail.debug", "false");
			props.put("mail.smtp.ehlo", "false");
			props.put("mail.smtp.timeout", smtpTimeout + "");
			props.put("mail.smtp.connectiontimeout", connectionTimeout + "");
			props.put("mail.smtp.sendpartial", String.valueOf(sendPartial));
			mailSession = Session.getInstance(props, null);
		}

		tMailSender = new Thread(this);
		tMailSender.start();
	}

	/**
	 * 从配置中读取信息
	 * 
	 * @throws ServletException
	 */
	protected void initParams() {
		try {
			ocsProperties.load(FOCSMailSenderServlet.class.getResourceAsStream("/mail.properties"));
			String mqp = ocsProperties.getProperty("mail-queue-path");
			if (StringUtils.isNotEmpty(mqp) && StringUtils.isNumeric(mqp))
				mail_queue_path = mqp;

			String sh = ocsProperties.getProperty("smtp_host");
			if (StringUtils.isNotEmpty(sh) && StringUtils.isNumeric(sh))
				smtp_host = sh;

			String sp = ocsProperties.getProperty("smtp_port");
			if (StringUtils.isNotEmpty(sp) && StringUtils.isNumeric(sp))
				smtp_port = Integer.parseInt(sp);

			String su = ocsProperties.getProperty("smtp_user");
			if (StringUtils.isNotEmpty(su) && StringUtils.isNumeric(su))
				smtp_user = su;

			String spw = ocsProperties.getProperty("smtp_pass");
			if (StringUtils.isNotEmpty(spw) && StringUtils.isNumeric(spw))
				smtp_pass = spw;
		} catch (Exception e) {
			this.log("Unabled to init mail.properties...");
		}
		
		if (mail_queue_path.startsWith(Globals.LOCAL_PATH_PREFIX)) {
			mail_queue_path = mail_queue_path.substring(Globals.LOCAL_PATH_PREFIX.length());
		} else if (mail_queue_path.startsWith("/")) {
			mail_queue_path = getServletContext().getRealPath(mail_queue_path);
		}
		if (!mail_queue_path.endsWith(File.separator))
			mail_queue_path += File.separator;
		queue = MailTransportQueue.getInstance(mail_queue_path);
		getServletContext().setAttribute(Globals.MAIL_QUEUE, queue);
	}

	/**
	 * 邮件传送线程入口
	 */
	public void run() {
		while (!stop) {
			long timeToSleep = 1000;
			List<MimeMessage> mails = new ArrayList<MimeMessage>();
			int i = 0;
			try {
				int mailc = queue.read(mailSession, mails, null, 1);
				if (mailc > 1)
					timeToSleep = 0;
				if (mails.size() > 0) {
					for (; i < mails.size(); i++) {
						MimeMessage mail = (MimeMessage) mails.get(i);
						if (userDNSQuery) {
							// 特快专递
							String email = mail.getRecipients(RecipientType.TO)[0].toString();
							String domain_name = parseDomain(email);
							// TODO: 实现域名的缓存,加快解析速度
							Lookup lookup = new Lookup(domain_name, Type.MX);
							lookup.run();
							if (lookup.getResult() != Lookup.SUCCESSFUL) {
								log("ERROR: " + lookup.getErrorString() + " when lookup MX record of " + email);
								continue;
							}
							Record[] answers = lookup.getAnswers();
							for (int ai = 0; ai < answers.length; ai++) {
								Transport transport = null;
								log("Using " + answers[i].getAdditionalName() + " to send mail to " + email);
								String mx_host = answers[i].getAdditionalName().toString();
								mailSession.getProperties().put("mail.smtp.host", mx_host);
								InternetAddress smtp_host = new InternetAddress(mx_host);
								try {
									transport = mailSession.getTransport(smtp_host);
									try {
										transport.connect();
										log("INFO: connected to " + mx_host);
									} catch (MessagingException me) {
										me.printStackTrace();
										log("ERROR: Connecto to " + mx_host + " failed.", me);
										continue;
									}
									InternetAddress mailToAddress = new InternetAddress(email);
									transport.sendMessage(mail, new InternetAddress[] { mailToAddress });
									log("INFO: mail sent to " + email);
									break;
								} finally {
									if (transport != null) {
										transport.close();
										transport = null;
									}
								}
							}
						} else {
							// 使用指定邮件帐号发送
							Transport.send(mail);
						}
					}
				}
			} catch (Exception e) {
				log("ERROR: 邮件传送失败,详细信息如下,", e);
			} finally {
				mails = null;
				if (timeToSleep > 0) {
					try {
						Thread.sleep(timeToSleep);
					} catch (InterruptedException e) {
					}
				}
			}
		}
	}

	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {

	}

	/**
	 * 停止邮件传送线程
	 */
	public void destroy() {
		stop = true;
		if (tMailSender != null) {
			try {
				tMailSender.join(10000, 100);
			} catch (InterruptedException e) {
			}
		}
		super.destroy();
	}

	/**
	 * 从邮件地址中解析出域名
	 * 
	 * @param email
	 * @return
	 */
	private static String parseDomain(String email) {
		String domain = null;
		if (email != null) {
			int idx = email.indexOf('@');
			if (idx != -1) {
				idx++;
				if (idx < email.length())
					domain = email.substring(idx);
			}
		}
		return domain;
	}

	private static long smtpTimeout = 600000;
	private static int connectionTimeout = 60000;
	private static boolean sendPartial = false;
}
